#include "Command.h"
#include "errhandle.h"
#include "memrec.h"
#include "GeneralException.h"
#define new DBG_NEW


tMsg::tMsg():MsgID(0), 
			 Data1(0), Data2(0),
			 Data3(0), Data4(0),
			 DataPtr(NULL), DataSize(0),
			 SyncCommand(NULL),
			 Ref(true),
			 Param(NULL)
{

}

int tMsg::Load(tStreamBuffer & msgdata)
{
	MsgID = msgdata.GetInt4();
	Data1 = msgdata.GetInt4();
	Data2 = msgdata.GetInt4();
	Data3 = msgdata.GetInt8();
	Data4 = msgdata.GetInt8();
	Data128.m_High64 = msgdata.GetInt8();
	Data128.m_Low64 = msgdata.GetInt8();
	DataSize = msgdata.GetInt4();
    if (DataPtr)
    {
		char* p = (char*)DataPtr;
		delete[] p;
    }
    DataPtr = NULL;
    if (DataSize != 0)
    {
		unsigned char* p = new BYTE[DataSize];
		if (p ==0)
			throw GeneralException(E_MEMALLOC_FAIL);
		SetFreeFlag(true);
		DataPtr = p;
		msgdata.GetData((unsigned char*)DataPtr, DataSize);
    }
    return 0;
}

int tMsg::Save(tStreamBuffer & msgdata)
{
	ALLOC(&msgdata, 40 + DataSize);
	msgdata.WriteInt4(MsgID);
	msgdata.WriteInt4(Data1);
	msgdata.WriteInt4(Data2);
	msgdata.WriteInt8(Data3);
	msgdata.WriteInt8(Data4);
	msgdata.WriteInt8(Data128.m_High64);
	msgdata.WriteInt8(Data128.m_Low64);
	msgdata.WriteInt4(DataSize);
	if (DataSize != 0)
	{
		msgdata.Append((unsigned char*)DataPtr, DataSize);
	}
    return 0;
}

void tMsg::SyncSender()
{
    if (SyncCommand)
    {
		tMsg msg;
		SyncCommand->SendCommand(msg, false);
    }
}
void tMsg::SetFreeFlag(bool free)
{
	Ref = !free;
}

void tMsg::FreeData()
{
    if (DataPtr && !Ref)
    {
		char * del = (char*)DataPtr;
		delete[] del;
    }
    DataPtr = 0;
	SetFreeFlag(false);
}

void tMsg::ReleaseParam()
{
	if (Param != NULL)
	{
		delete Param;
	}
	Param = NULL;
}

tCommand::tCommand(void)
{
#ifndef _linux_
    hQueueLock = CreateMutex(NULL, FALSE, NULL);
    hNewMsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
#endif
	
}

tCommand::~tCommand(void)
{
    LockQueue();
    // clear the msg loop
    //...
    UnLockQueue();
#ifndef _linux_
    CloseHandle(hQueueLock);
	CloseHandle(hNewMsgEvent);
	tMsg msg;
	while (!MsgQueue.empty())
	{
		msg = MsgQueue.front();
		MsgQueue.pop();
		DBGSTR1("Leak Message: %u\n", msg.MsgID);
		msg.FreeData();
		msg.ReleaseParam();
	}
#endif
}

void tCommand::LockQueue()
{
#ifndef _linux_
	WaitForSingleObject(hQueueLock, INFINITE);
#else
	QueueLock.Wait();
#endif
}

void tCommand::UnLockQueue()
{
#ifndef _linux_
	ReleaseMutex(hQueueLock);
#else
	QueueLock.Release();
#endif
}

// Get next command in the msg loop
int tCommand::GetCommand(tMsg* msg, bool bWait, unsigned int wait_time)
{
    msg->MsgID = CMD_MSG_NULL;
	msg->DataPtr = NULL;
	msg->Ref = true;
	msg->SyncCommand = NULL;
	msg->DataSize = 0;
	msg->Data1 = 0;
	msg->Data2 = 0;
	msg->Data3 = 0;
	msg->Data4 = 0;
	msg->Param = NULL;
#ifndef _linux_
    LockQueue();
    if (MsgQueue.size() == 0)
    {
		if (bWait)
		{
			ResetEvent(hNewMsgEvent);
			UnLockQueue();
			//Wait new msg to come.
			if (wait_time == 0)
				WaitForSingleObject(hNewMsgEvent, INFINITE);
			else 
			{
				int r =WaitForSingleObject(hNewMsgEvent, wait_time);
				if (r == WAIT_TIMEOUT)
				{
					msg->MsgID = CMD_MSG_WAIT_TIMEOUT;
					return 1;
				}
			}
			LockQueue();
		}
		else
		{
			UnLockQueue();
			return -1;
		}
		// Get the next msg from queue.
		*msg = MsgQueue.front();
		MsgQueue.pop();
    }
    else
    {
		// Get the next msg from queue.
		*msg = MsgQueue.front();
		MsgQueue.pop();
    }
    UnLockQueue();
#else
    if (bWait)
    {
		if (wait_time == 0)
		{
			int r = Sema.Wait();
			assert(r == 0);
		}
		else
		{
			int r = Sema.WaitTime(wait_time);
			if (r == 1)  //Time out.
			{
				msg->MsgID = CMD_MSG_WAIT_TIMEOUT;
				return 1;
			}
			assert (r == 0);	    
		}
		LockQueue();
		assert(MsgQueue.size() > 0);
		*msg = MsgQueue.front();
		MsgQueue.pop();
		UnLockQueue();
    }
    else
    {
		int r = Sema.Try();
		if (r == 0)
		{
			LockQueue();
			*msg = MsgQueue.front();
			MsgQueue.pop();
			UnLockQueue();
		}
    }
#endif
    if (msg->MsgID == CMD_MSG_EXIT)
		return 0;
    else
		return 1;
}

int tCommand::SendCommand(tMsg msg, bool bWait)
{
    LockQueue();
    tCommand Sync;
    if (bWait)
		msg.SyncCommand = &Sync;
    MsgQueue.push(msg);
#ifndef _linux_
    SetEvent(hNewMsgEvent);
    UnLockQueue();
#else
    UnLockQueue();
    Sema.Post();
#endif
    if (bWait)
    {
		tMsg msg_temp;
		Sync.GetCommand(&msg_temp, true);
    }
    return 0;
}

void tCommand::FlushCommandQueue()
{
    LockQueue();
	while (MsgQueue.size() != 0)
	{
		tMsg & msg = MsgQueue.front();
		msg.ReleaseParam();
		MsgQueue.pop();
	}
	UnLockQueue();
}


